package com.basker.pisces.domain.command.meta;

import java.util.ArrayList;
import java.util.List;

import org.apache.commons.lang3.ArrayUtils;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

import com.basker.pisces.context.LoginContext;
import com.basker.pisces.core.meta.annotation.DataField;
import com.basker.pisces.domain.ISupportEntityBuild;
import com.basker.pisces.domain.entity.meta.Entity;
import com.basker.pisces.domain.property.meta.Property;
import com.basker.pisces.domain.property.meta.StatusProperty;
import com.basker.pisces.domain.value.meta.StatusOption;

/**
 * 更新实体状态的命令，对于添加了该命令的实体，必须定义状态属性{@link StatusProperty}
 * <p>
 *
 * <li>默认加对象锁，参见{@link #getEnableLock()}
 * <li>默认重加载数据，参见{@link #isRequireReload()}
 * <li>支持记录执行时间和执行人属性
 *
 * @author hangwen
 */
public class ChangeStatusCommand extends TransactionalCommand implements ISupportEntityBuild {

    @DataField
    private String statusPropertyAlias;
    @DataField
    private List<Integer> validStatuses = new ArrayList<>();
    @DataField
    private Integer targetStatus;
    @DataField
    private String executeTimePropertyAlias;
    @DataField
    private String executeUserPropertyAlias;
    @DataField
    private String invalidStatusMessagePattern;

    public ChangeStatusCommand() {
        this.setEnableLock(true);
        this.setRequireReload(true);
    }

    @Override
    public void build(Entity entity) {
        StatusProperty statusProperty = this.findStatusProperty(entity);

        Assert.notNull(statusProperty, "Entity has no Status Property");
        Assert.notNull(targetStatus, "'targetStatus' is required");

        StatusOption targetOption = statusProperty.getOption(targetStatus);
        if (targetOption == null) {
            throw new IllegalStateException(
                    statusProperty.toString() + " targetStatus '" + targetStatus + "'  is invalid.");
        }

        // 添加需要组装的属性
        this.addRequiredPropertyAlias(statusProperty.getAlias());

        if (this.executeTimePropertyAlias != null) {
            Property<?> executeTimeProperty = entity.getProperty(this.executeTimePropertyAlias, true);
            this.addRequiredPropertyAlias(executeTimeProperty.getAlias());
        }

        if (this.executeUserPropertyAlias != null) {
            Property<?> executeUserProperty = entity.getProperty(this.executeUserPropertyAlias, true);
            this.addRequiredPropertyAlias(executeUserProperty.getAlias());
        }
    }

    /**
     * 获取存储执行此命令时间的属性，如果设置，会在命令执行前为该字段赋值
     *
     * @return
     */
    public String getExecuteTimePropertyAlias() {
        return executeTimePropertyAlias;
    }

    /**
     * 获取存储执行此命令用户的属性，如果设置，会在命令执行前为该字段赋值，用户来源为{@link LoginContext#getUserId()}
     *
     * @return
     */
    public String getExecuteUserPropertyAlias() {
        return executeUserPropertyAlias;
    }

    /**
     * 获取描述非法状态的消息,支持${xxx}的格式以便从资源文件获取
     *
     * @return
     */
    public String getInvalidStatusMessagePattern() {
        return invalidStatusMessagePattern;
    }

    /**
     * 获取需要修改的状态属性，如果实体存在唯一的状态属性，可以不配置
     *
     * @return
     */
    public String getStatusPropertyAlias() {
        return statusPropertyAlias;
    }

    public Integer getTargetStatus() {
        return targetStatus;
    }

    /**
     * 获取执行当前命令的合法状态
     *
     * <p>
     * 获取当前对象的可以执行命令的合法状态，只有状态与返回值匹配的记录才可以正常执行，如果返回null或者空集合，则不进行有效状态的判断
     *
     * @return
     */
    public List<Integer> getValidStatuses() {
        return validStatuses;
    }

    public void setExecuteTimePropertyAlias(String executeTimePropertyAlias) {
        this.executeTimePropertyAlias = executeTimePropertyAlias;
    }

    public void setExecuteUserPropertyAlias(String executeUserPropertyAlias) {
        this.executeUserPropertyAlias = executeUserPropertyAlias;
    }

    public void setInvalidStatusMessagePattern(String invalidStatusMessagePattern) {
        this.invalidStatusMessagePattern = invalidStatusMessagePattern;
    }

    public void setStatusPropertyAlias(String statusPropertyAlias) {
        this.statusPropertyAlias = statusPropertyAlias;
    }

    public void setTargetStatus(Integer targetStatus) {
        this.targetStatus = targetStatus;
    }

    public void setValidStatuses(List<Integer> validStatuses) {
        this.validStatuses = validStatuses;
    }

    private StatusProperty findStatusProperty(Entity entity) {
        String statusPropertyAlias = this.getStatusPropertyAlias();

        if (StringUtils.isEmpty(statusPropertyAlias)) {
            StatusProperty[] statusProperties = entity.getStatusProperties();
            int length = ArrayUtils.getLength(statusProperties);

            if (length == 1) {
                this.setStatusPropertyAlias(statusProperties[0].getAlias());
                return statusProperties[0];
            }

            return null;
        } else {
            Property<?> property = entity.getPropertyByAlias(statusPropertyAlias);

            if (property instanceof StatusProperty) {
                return (StatusProperty) property;
            }

            return null;
        }
    }

}
